﻿using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.ActionManagement;
using JetBrains.Annotations;
using JetBrains.Application;
using JetBrains.Application.DataContext;
using JetBrains.DataFlow;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.ContextNavigation;
using JetBrains.ReSharper.Feature.Services.Occurences;
using JetBrains.ReSharper.Feature.Services.Search;
using JetBrains.ReSharper.Features.Finding.ExecutionHosting;
using JetBrains.ReSharper.Features.Finding.GoToDeclaredElement;
using JetBrains.ReSharper.Psi;
using JetBrains.TextControl;
using JetBrains.Util;
using DataConstants = JetBrains.ProjectModel.DataContext.DataConstants;
using ShellComponentsEx = JetBrains.UI.Application.ShellComponentsEx;

namespace MugenMvvmToolkit.ReSharper.Navigations
{
    [ContextNavigationProvider]
    public class ViewViewModelNavigationProvider : INavigateFromHereProvider
    {
        #region Fields

        private readonly IShellLocks _locks;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="ViewViewModelNavigationProvider" /> class.
        /// </summary>
        public ViewViewModelNavigationProvider([NotNull] IShellLocks locks)
        {
            if (locks == null) throw new ArgumentNullException("locks");
            _locks = locks;
        }

        #endregion

        #region Implementation of IWorkflowProvider<ContextNavigation,out NavigationActionGroup>

        public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)
        {
            ISolution solution = dataContext.GetData(DataConstants.SOLUTION);
            var declaredElement = dataContext.GetData(JetBrains.ReSharper.Psi.Services.DataConstants.TYPE_OR_TYPE_MEMBER);
            if (solution == null || declaredElement == null)
                return Emtpy;
            var @class = declaredElement as IClass ?? declaredElement.GetContainingType() as IClass;
            if (@class == null)
                return Emtpy;

            TypeProvider provider = TypeProvider.Create(@class.Module, @class.ResolveContext);
            if (@class.IsOfType(provider.ViewModelInterface))
            {
                return new[]
                {
                    new ContextNavigation(UiResources.NavigateToView, ActionIdConstants.ViewViewModelNavigationAction,
                        NavigationActionGroup.Important, () => GoToView(dataContext, solution, @class, provider))
                };
            }

            if (@class.IsOfType(provider.ViewInterface))
            {
                return new[]
                {
                    new ContextNavigation(UiResources.NavigateToViewModel,
                        ActionIdConstants.ViewViewModelNavigationAction, NavigationActionGroup.Important,
                        () => GoToViewModel(dataContext, solution, @class, provider))
                };
            }
            return Emtpy;
        }

        #endregion

        #region Methods

        private static IEnumerable<ContextNavigation> Emtpy
        {
            get { return Enumerable.Empty<ContextNavigation>(); }
        }

        private void GoToView(IDataContext context, ISolution solution, IClass viewModelClass, TypeProvider typeProvider)
        {
            ITextControl data = context.GetData(JetBrains.TextControl.DataContext.DataConstants.TEXT_CONTROL);
            if (data == null)
                return;

            var list = typeProvider
                .FindViewsForViewModel(viewModelClass)
                .Select(@class => new DeclaredElementOccurence(@class))
                .Cast<IOccurence>()
                .ToList();
            if (list.IsEmpty())
            {
                ShellComponentsEx.Tooltips(Shell.Instance.Components)
                    .ShowAtCaret(EternalLifetime.Instance, UiResources.NavigateToViewEmpty, data, _locks,
                        Shell.Instance.GetComponent<IActionManager>());
            }
            else
            {
                DefaultNavigationExecutionHost.Instance.ShowContextPopupMenu(context, list,
                    () =>
                        new GotoDeclaredElementsBrowserDescriptor(solution,
                            ActionIdConstants.ViewViewModelNavigationAction, "IView", list),
                    OccurencePresentationOptions.DefaultOptions, true, UiResources.NavigateToViewModel);
            }
        }

        private void GoToViewModel(IDataContext context, ISolution solution, IClass viewClass, TypeProvider typeProvider)
        {
            ITextControl data = context.GetData(JetBrains.TextControl.DataContext.DataConstants.TEXT_CONTROL);
            if (data == null)
                return;

            var list = new List<IOccurence>();
            foreach (IDeclaredType declaredType in typeProvider.FindViewModelsForView(viewClass))
            {
                ITypeElement typeElement = declaredType.GetTypeElement();
                if (typeElement == null)
                    continue;
                list.Add(new DeclaredElementOccurence(typeElement));
            }
            if (list.IsEmpty())
            {
                ShellComponentsEx.Tooltips(Shell.Instance.Components)
                    .ShowAtCaret(EternalLifetime.Instance, UiResources.NavigateToViewModelEmpty, data, _locks,
                        Shell.Instance.GetComponent<IActionManager>());
            }
            else
            {
                DefaultNavigationExecutionHost.Instance.ShowContextPopupMenu(context, list,
                    () =>
                        new GotoDeclaredElementsBrowserDescriptor(solution,
                            ActionIdConstants.ViewViewModelNavigationAction, "IViewModel", list),
                    OccurencePresentationOptions.DefaultOptions, true, UiResources.NavigateToViewModel);
            }
        }

        #endregion
    }
}